/*
 * keyed_set.c
 *
 * Maintains a set of objects and performs standard operations on them.
 *
 * $Id: keyed_set.c 927 2006-09-03 06:17:24Z rpz $
 *
 * Bob Zimmermann <rpz.cse.wustl.edu> Totally free.
 */

#include "keyed_set.h"

static void _keyed_set_bail();
static da_pos_t _keyed_set_get_ind(keyed_set_t * ks, da_pos_t id, int die_on_err);

keyed_set_ops_t *
keyed_set_ops_new(keyed_set_copy_op copy, keyed_set_delete_op delete)
{
  keyed_set_ops_t *kso;
  kso = malloc(sizeof(keyed_set_ops_t));
  if (kso == NULL)
    _keyed_set_bail();

  kso->copy = copy;
  kso->delete = delete;

  return kso;
}

keyed_set_ops_t *
keyed_set_ops_copy(const keyed_set_ops_t * kso)
{
  keyed_set_ops_t *copy;
  copy = malloc(sizeof(keyed_set_ops_t));

  copy->copy = kso->copy;
  copy->delete = kso->delete;

  return copy;
}

void
keyed_set_ops_delete(keyed_set_ops_t * kso, int befrei)
{
  if (befrei)
    free(kso);
}

void *
_keyed_set_copy_ind(const void *ind_p)
{
  da_pos_t *copy = malloc(sizeof(da_pos_t));
  *copy = *((da_pos_t *) ind_p);
  return (void *) copy;
}


/* keyed_set_new Create a new, empty set. */
keyed_set_t *
keyed_set_new(ks_pos_t init_size, keyed_set_ops_t * kso)
{
  keyed_set_t *ks;
  darray_ops_t *ops, *set_ops;

  ops = darray_ops_new(_keyed_set_copy_ind, NULL, NULL, mem_free);

  if (kso != NULL) {
    set_ops = darray_ops_new(kso->copy, NULL, NULL, kso->delete);

    /* currently we only use this as a messanger to darray, but we want to
     * keep our data structures consistent: always turn over memory to the
     * data structures that you pass them to. */
    free(kso);
  }
  else
    set_ops = NULL;

  ks = malloc(sizeof(keyed_set_t));
  if (ks == NULL)
    _keyed_set_bail();

  ks->inds = darray_new(init_size, ops);
  ks->set = darray_new(init_size, set_ops);

  return ks;
}

/* keyed_set_copy Copy all contents of a keyed_set to a new one. */
keyed_set_t *
keyed_set_copy(const keyed_set_t * ks)
{
  keyed_set_t *copy;

  copy = malloc(sizeof(keyed_set_t));
  if (copy == NULL)
    _keyed_set_bail();

  copy->inds = darray_copy(ks->inds);
  copy->set = darray_copy(ks->set);

  return copy;
}

/* keyed_set_delete Free the memory allocated by the keyed_set */
void
keyed_set_delete(keyed_set_t * ks, int befrei)
{
  if (ks->inds != NULL)
    darray_delete(ks->inds, 1);
  ks->inds = NULL;
  if (ks->set != NULL)
    darray_delete(ks->set, 1);
  ks->set = NULL;
  if (befrei)
    free(ks);
}

/*
 * keyed_set_add
 *
 * Add a new darray to the darray set.  All darrays are keyed by ids, and this
 * new darray will be forever keyed by the passed id.
 */
void
keyed_set_add(keyed_set_t * ks, void *data, ks_pos_t id)
{
  da_pos_t *l_id, *set_ind, toadd;

  toadd = id + 1 - darray_size(ks->inds);
  while (toadd-- > 0) {
    l_id = malloc(sizeof(da_pos_t));
    *l_id = -1;
    darray_add(ks->inds, l_id);
  }

  if (_keyed_set_get_ind(ks, id, 0) != -1) {
    fprintf(stderr, "Attempt to clobber a set with id %d.\n", id);
    exit(1);
  }
  set_ind = darray_get(ks->inds, id);
  *set_ind = darray_size(ks->set);
  /*darray_set(ks->inds, id, size); */

  darray_add(ks->set, data);
}

/*
 * keyed_set_get Get object from set with the given id.  If no such object
 * exists, NULL is returned.
 */
void *
keyed_set_get(keyed_set_t * ks, ks_pos_t id)
{
  da_pos_t ind = _keyed_set_get_ind(ks, id, 0);
  if (ind >= 0)
    return darray_get(ks->set, ind);
  else
    return NULL;
}

darray_t *
keyed_set_get_darray(keyed_set_t * ks)
{
  return ks->set;
}

/* keyed_set_set Set the value of the object with the given id. */
void
keyed_set_set(keyed_set_t * ks, ks_pos_t id, void *data)
{
  darray_set(ks->set, _keyed_set_get_ind(ks, id, 1), data);
}

da_pos_t
_keyed_set_get_ind(keyed_set_t * ks, da_pos_t id, int die_on_err)
{
  da_pos_t ind, sz;

  sz = darray_size(ks->inds);
  if (id >= sz) {
    if (die_on_err)
      goto BAIL;
    else
      return darray_size(ks->inds);
  }
  ind = *((da_pos_t *) darray_get(ks->inds, id));
  if (die_on_err && ind < 0)
    goto BAIL;

  return ind;

BAIL:
  fprintf(stderr, "keyed_set: Referencing an uninitialized object.\n");
  exit(1);
}

void
_keyed_set_bail()
{
  fprintf(stderr, "keyed_set: Out of memory.\n");
  exit(1);
}
